/**
 *
 */
package jp.seraph.jspf.codec;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import jp.seraph.jspf.pose.Pose;
import jp.seraph.jspf.pose.PoseManager;

/**
 * ファイルシステムとPoseのマッピングを行うマネージャ
 * 
 */
public class FilePoseManager implements PoseManager {
    public FilePoseManager(File aBaseDirectory, PoseCodec aCodec, boolean mLoadAll) {
        mCodec = aCodec;
        mBaseDirectory = aBaseDirectory;
        mPoseMap = new HashMap<String, Pose>();

        if (!mBaseDirectory.exists())
            mBaseDirectory.mkdir();

        if (mLoadAll)
            this.loadAll();
    }

    private PoseCodec mCodec;
    private File mBaseDirectory;
    private Map<String, Pose> mPoseMap;
    private static final String SUFFIX = ".pose";

    private File createFileInstance(String aPoseName) {
        return new File(mBaseDirectory, aPoseName + SUFFIX);
    }

    public void save() throws IOException {
        for (String tPoseName : mPoseMap.keySet()) {
            this.save(tPoseName);
        }
    }

    private void save(String aPoseName) throws IOException {
        Pose tTargetPose = this.getPose(aPoseName);
        File tSaveFile = new File(mBaseDirectory, aPoseName + SUFFIX);
        if (tSaveFile.exists())
            tSaveFile.delete();
        tSaveFile.createNewFile();

        Writer tWriter = null;

        try {
            tWriter = new OutputStreamWriter(FileUtils.openOutputStream(tSaveFile));
            mCodec.encode(tTargetPose, tWriter);
        } finally {
            IOUtils.closeQuietly(tWriter);
        }
    }

    private void loadAll() {
        FilenameFilter tFilter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(SUFFIX);
            }
        };
        for (File tFile : mBaseDirectory.listFiles(tFilter)) {
            if (tFile.isFile())
                loadPose(tFile);
        }
    }

    private void loadPose(File aPoseFile) {
        String tName = aPoseFile.getName().replaceAll(SUFFIX + "$", "");
        Reader tReader = null;

        try {
            tReader = new InputStreamReader(FileUtils.openInputStream(aPoseFile));
            Pose tPose = mCodec.decode(tReader);
            this.setPose(tName, tPose);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(tReader);
        }
    }

    /**
     * 
     * @see jp.seraph.jspf.pose.PoseManager#setPose(java.lang.String,
     *      jp.seraph.jspf.pose.Pose)
     */

    public void setPose(String aName, Pose aPose) {
        mPoseMap.put(aName, aPose);

        try {
            this.save(aName);
        } catch (IOException e) {
            throw new Error(e);
        }
    }

    /**
     * 
     * @see jp.seraph.jspf.pose.PoseManager#containsPose(java.lang.String)
     */

    public boolean containsPose(String aName) {
        if (mPoseMap.containsKey(aName))
            return true;

        return new File(mBaseDirectory, aName + SUFFIX).exists();
    }

    /**
     * 
     * @see jp.seraph.jspf.pose.PoseManager#getPose(java.lang.String)
     */

    public Pose getPose(String aName) {
        if (mPoseMap.containsKey(aName))
            return mPoseMap.get(aName);

        if (this.containsPose(aName)) {
            this.loadPose(new File(mBaseDirectory, aName + SUFFIX));
            return mPoseMap.get(aName);
        } else {
            return null;
        }
    }

    /**
     * 
     * @see jp.seraph.jspf.pose.PoseManager#removePose(java.lang.String)
     */

    public void removePose(String aName) {
        mPoseMap.remove(aName);
        createFileInstance(aName).delete();
    }

    /**
     * 
     * @see jp.seraph.jspf.pose.PoseManager#getPoseNames()
     */

    public Iterable<String> getPoseNames() {
        List<String> tResult = new ArrayList<String>(mPoseMap.keySet());
        Collections.sort(tResult);
        return tResult;
    }
}
